﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Security.Cryptography;

namespace FileComparer
{
    class Program
    {
        static void Main(string[] args)
        {
            if (args.Length >= 2)
            {
                string filename1 = args[0];
                string filename2 = args[1];

                Console.WriteLine("***** File Comparer *****");
                Console.WriteLine("Comapring Files:{2}{0}{2}{1}{2}", filename1, filename2, Environment.NewLine);

                bool file1Exist = File.Exists(filename1);
                bool file2Exist = File.Exists(filename2);

                if (file1Exist && file2Exist)
                {
                    bool hashesSame = CompareHashes(filename1, filename2);

                    Console.WriteLine();

                    bool sizesSame = ComapreSizes(filename1, filename2);

                    if (sizesSame && !hashesSame)
                    {
                        Console.WriteLine();
                        Console.WriteLine("Files have the same size but differnt hashes. Would you like to do a byte to byte compare? [y/n]");
                        int key = Console.Read();
                        if (Convert.ToChar(key) == 'y' || Convert.ToChar(key) == 'Y')
                        {
                            ByteToByteComapre(filename1, filename2);
                        }
                    }
                }
                else
                {
                    if (!file1Exist)
                    {
                        Console.WriteLine("{0} does not exist.", filename1);
                    }

                    if (!file2Exist)
                    {
                        Console.WriteLine("{0} does not exist.", filename2);
                    }
                }
            }
            else
            {
                Console.WriteLine("Must have to arguments.");
            }

            Console.WriteLine();
            Console.WriteLine("Press any key to continue...");
            Console.ReadKey();
        }

        /// <summary>
        /// Compares the sizes of two files
        /// </summary>
        /// <param name="filename1">File 1</param>
        /// <param name="filename2">File 2</param>
        /// <returns>True if they are the same, otherwise false</returns>
        private static bool ComapreSizes(string filename1, string filename2)
        {
            // Compare file sizes
            Console.WriteLine("Comparing File Sizes.");
            FileInfo fi1 = new FileInfo(filename1);
            Console.WriteLine("File{0} size: {1}", 1, fi1.Length);
            FileInfo fi2 = new FileInfo(filename2);
            Console.WriteLine("File{0} size: {1}", 2, fi2.Length);
            if (fi1.Length == fi2.Length)
            {
                Console.WriteLine("File sizes are the same.");
                return true;
            }
            else
            {
                Console.WriteLine("File sizes are not the same.");
                return false;
            }
        }
        
        /// <summary>
        /// Comapres the hashes of two files
        /// </summary>
        /// <param name="filename1">File 1</param>
        /// <param name="filename2">File 2</param>
        /// <returns>True if they are the same, otherwise false</returns>
        private static bool CompareHashes(string filename1, string filename2)
        {
            // Compare the hashes
            Console.WriteLine("Comparing File Hashes.");
            byte[] fileHash1 = GetFileHash(filename1);
            Console.WriteLine("File{0} hash: {1}", 1, BitConverter.ToString(fileHash1));
            byte[] fileHash2 = GetFileHash(filename2);
            Console.WriteLine("File{0} hash: {1}", 2, BitConverter.ToString(fileHash2));
            if (BitConverter.ToString(fileHash1) == BitConverter.ToString(fileHash2))
            {
                Console.WriteLine("Hashes match.");
                return true;
            }
            else
            {
                Console.WriteLine("Hashes do not match.");
                return false;
            }
        }

        /// <summary>
        /// Does a byte to byte comapre of a file
        /// </summary>
        /// <param name="filename1">File 1</param>
        /// <param name="filename2">File 2</param>
        private static void ByteToByteComapre(string filename1, string filename2)
        {
            Console.WriteLine("Comparing Individual Bytes.");
            Console.WriteLine("{0,-13}{1,-8}{2,-8}", "Position", "File1", "File2");
            using (FileStream fs1 = new FileStream(filename1, FileMode.Open),
                fs2 = new FileStream(filename2, FileMode.Open))
            {
                while (fs1.Position < fs1.Length &&
                    fs2.Position < fs2.Length)
                {
                    int file1Byte = fs1.ReadByte();
                    int file2Byte = fs2.ReadByte();

                    if (file1Byte != file2Byte)
                    {
                        Console.WriteLine("{0,-13}{1,-8}{2,-8}", fs1.Position, file1Byte, file2Byte);
                    }
                }
            }
        }

        /// <summary>
        /// Creates a Hash of a file
        /// </summary>
        /// <param name="filename">The name of the file to hash</param>
        /// <returns>The file's hash</returns>
        private static byte[] GetFileHash(string filename)
        {
            using (HashAlgorithm hashAlg = HashAlgorithm.Create())
            {
                using (FileStream fsA = new FileStream(filename, FileMode.Open))
                {
                    return hashAlg.ComputeHash(fsA);
                }
            }
        }
    }
}
